/*
 * File:   libeap_md5.c
 * Author: george
 *
 * Created on June 13, 2009, 6:04 PM
 */

#include <stdio.h>
#include <stdlib.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/md5.h>
#include <openssl/eap.h>
#include <openssl/buffer.h>
#include <openssl/rand.h>
#include "libeap_md5.h"
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

/*
 *
 */


int find_password(FILE* f,unsigned char* id,int idlen,
                        unsigned char* pwd,int *pwdlen){

     char temp_line[256];
     fseek(f,0,SEEK_SET);

     memset(temp_line,'\0',sizeof(temp_line));

     while(!feof(f)) {
         fgets(temp_line,sizeof(temp_line),f);
         if(strlen(temp_line)==idlen+1)
             if(memcmp(temp_line,id,idlen)==0){
                 if(feof(f)) goto err;
                 fgets(temp_line,sizeof(temp_line),f);
                 (*pwdlen)=strlen(temp_line)-1;
                 memcpy(pwd,temp_line,*pwdlen);
                 return(1);
             }
         fgets(temp_line,sizeof(temp_line),f);
     }
err:
    return(0);
}


unsigned char *chap_md5(unsigned char id,unsigned char *pwd,int pwd_len,
                        char* challenge,int challenge_len,int *olen)
{
    const EVP_MD *m;
    EVP_MD_CTX    ctx;
    unsigned char *ret;
    //DEBUG
    int i;
    unsigned char prc;
    printf("\n\r md5 data\n\r");
    printf("id: %d\n\r",(int)id);
    printf("challenge: %d",(int)challenge[0]);
    for(i=1;i<challenge_len;i++){
        printf(",%d",(int)challenge[i]);
    }
    printf("\n\n\r");
    pwd[pwd_len] = '\0';
    printf("password: %s",pwd);


    printf("\n\rend of md5 data\n\r");
    //DEBUG
    OpenSSL_add_all_digests();
    m = EVP_md5();
    if (!(ret = (unsigned char *)OPENSSL_malloc(EVP_MAX_MD_SIZE)))
        goto err;
    EVP_DigestInit(&ctx, m);
    EVP_DigestUpdate(&ctx, &id, sizeof(unsigned char));
    EVP_DigestUpdate(&ctx, pwd, pwd_len);
    EVP_DigestUpdate(&ctx, challenge, challenge_len);
    EVP_DigestFinal(&ctx, ret, olen);
    return ret;
err:
    return NULL;

}


        /* Information functions about EAP Methods */
char eap_get_type(void){
    return(4);
}
int eap_is_MA(void){
    return(0);
}
        /* Action functions for EAP Methods */
int eap_server_init(EAP *eap){
    MD5_CHAP_AUTHENTICATOR_DATA *chap;
    if(!BUF_MEM_grow(eap->raw_state_data,sizeof(MD5_CHAP_AUTHENTICATOR_DATA)))
        goto err;
    chap =(MD5_CHAP_AUTHENTICATOR_DATA *) eap->raw_state_data->data;
    chap->challenge = NULL;
    chap->state = AUTH_INITIAL;
    return(1);
err:
    return(0);
}
int eap_client_init(EAP *eap){
    MD5_CHAP_PEER_DATA *chap;
    if(!BUF_MEM_grow(eap->raw_state_data,sizeof(MD5_CHAP_PEER_DATA)))
        goto err;
    chap =(MD5_CHAP_PEER_DATA *) eap->raw_state_data->data;
    chap->challenge = NULL;
    chap->state = PEER_INITIAL;
    return(1);
err:
    return(0);
}
int eap_process_server_req(EAP *eap, char *type_data, int len, EAP_METH_RET * eap_ret){
	unsigned char *pos, *challenge;
	size_t challenge_len;
        MD5_CHAP_PEER_DATA *chap = (MD5_CHAP_PEER_DATA *) eap->raw_state_data->data;
        pos = type_data;
        if(chap->state!=PEER_INITIAL) goto err;


        //SETTING PASSWORD
        if(eap->credentials.pwd_len == 0){
            puts("specify password");
            scanf("%s", eap->credentials.pwd);
            eap->credentials.pwd_len = strlen(eap->credentials.pwd);
        }


        challenge_len = *pos++;
        if(challenge_len==0||challenge_len > len-1){
            eap_ret->ignore = 1;
            goto err;
        }
        
        challenge = OPENSSL_malloc(challenge_len);
        if(challenge==NULL)
            goto err;
        memcpy(challenge,pos,challenge_len);
        chap->challenge = challenge;
        chap->challenge_len = challenge_len;

        eap_ret->ignore = 0;
        eap_ret->decision = DECISION_UNCOND_SUCC;
        eap_ret->allowNotifications = 1;
        eap_ret->methodState = METHOD_DONE;
        chap->state = DONE;

        return(1);
err:
        return(0);

}

char get_eap_server_id(EAP *eap)
{
    if(!eap) return 0;
    return ( ((int) eap->id) + 1 ) % (sizeof(unsigned char) * 8);
}

int eap_process_client_res(EAP *eap, char *type_data, int len){
     char *p,*ret;
     int mac_len;
     int t_len;
     MD5_CHAP_AUTHENTICATOR_DATA *chap = (MD5_CHAP_AUTHENTICATOR_DATA *)eap->raw_state_data->data;
     if(chap->state!=WAITING_FOR_REPLY) goto err;

     if(eap->credentials.pwd_len == 0){
         FILE* f = fopen("/usr/local/MD5_CHAP/passwords","r");
         if(!f)
         {
            printf("%s\n", strerror(errno));
            goto err;
         }
         if(!find_password(f,eap->credentials.peer_id,
                 eap->credentials.peer_id_len,eap->credentials.pwd,
                 &(eap->credentials.pwd_len))) goto err;
         fclose(f);
     }


     ret = chap_md5(eap->id,eap->credentials.pwd,eap->credentials.pwd_len,
                    chap->challenge,chap->challenge_len,&mac_len);

     if(!ret) goto err;
     p = type_data;
     t_len = (*p); p++;
     if(t_len!=mac_len)
         chap->state = FAILURE;
     else{
         chap->state = SUCCESS;
         //if(memcmp(ret,p,mac_len)==0)
         //else chap->state = FAILURE;
     }
     OPENSSL_free(ret);
     return(1);
err:
     chap->state = FAILURE;
     return(0);
}
int eap_build_server_req(EAP *eap, BUF_MEM *bm, int offset){
     char *p;
     MD5_CHAP_AUTHENTICATOR_DATA *chap = (MD5_CHAP_AUTHENTICATOR_DATA *)eap->raw_state_data->data;
     if(chap->state!=AUTH_INITIAL)
         goto err;

     chap->challenge_len = CHALLENGE_LEN;
     chap->challenge = OPENSSL_malloc(chap->challenge_len);
     if(chap->challenge==NULL)
         goto err;

     if(!BUF_MEM_grow(bm,chap->challenge_len+1+offset))
         goto err;

     RAND_bytes(chap->challenge,chap->challenge_len);
     p=bm->data + offset;
     (*p) = CHALLENGE_LEN;p++;
     memcpy(p,chap->challenge,chap->challenge_len);
     chap->state = WAITING_FOR_REPLY;
     return(chap->challenge_len + 1);

err:
     if(chap->challenge!=NULL){
         OPENSSL_free(chap->challenge);
         chap->challenge = NULL;
     }
     return(0);
}
int eap_build_client_res(EAP *eap, BUF_MEM *bm, int offset){
    char *ret = NULL,*p;
    int  mac_len;
    MD5_CHAP_PEER_DATA *chap = (MD5_CHAP_PEER_DATA *) eap->raw_state_data->data;
    if(chap->state != DONE)
        goto err;


    ret = chap_md5(eap->id,
                   eap->credentials.pwd,
                   eap->credentials.pwd_len,
                    chap->challenge,
                   chap->challenge_len,
                   &mac_len);

    
    if(ret==NULL)
        goto err;
    
    if(mac_len>255)
        goto err;

    if(!BUF_MEM_grow(bm,mac_len+1+offset))
        goto err;
    p=bm->data;p+=offset;
    (*p)=mac_len;p++;
    memcpy(p,ret,mac_len);
    OPENSSL_free(ret);

    return(mac_len+1);

err:
    if(ret!=NULL)
        OPENSSL_free(ret);
    return(0);
}
int eap_is_server_done(EAP *eap){
    MD5_CHAP_AUTHENTICATOR_DATA *chap = (MD5_CHAP_AUTHENTICATOR_DATA *)eap->raw_state_data->data;
    return chap->state==SUCCESS||chap->state==FAILURE;
}
int eap_is_server_success(EAP *eap){
    MD5_CHAP_AUTHENTICATOR_DATA *chap = (MD5_CHAP_AUTHENTICATOR_DATA *)eap->raw_state_data->data;
    return chap->state==SUCCESS;
}
int eap_is_server_key_available(EAP *eap){
    return(0);
}
int eap_get_server_key(EAP* eap){
    return(1);
}
int eap_is_client_key_available(EAP *eap){
    return(0);
}
int eap_get_client_key(EAP* eap){
    return(1);
}
